#include "fsxwsmainwindow.h"
#include "ui_fsxwsmainwindow.h"
using namespace FsxWsGUI;

//
// Constructor
//
FsxWsMainWindow::FsxWsMainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::FsxWsMainWindow)
{
    ui->setupUi(this);
    FsxWsMainWindow::init();
}

//
// Destructor
//
FsxWsMainWindow::~FsxWsMainWindow()
{
    FsxWsMainWindow::terminate();
    delete ui;
}

//
// Terminate
//
void FsxWsMainWindow::terminate()
{
    // disable updates
    FsxWsMainWindow::_aircraftUpdateTimer->stop();

    // stop web server and siconnect it
    FsxWsMainWindow::stopWebServer();
    FsxWsMainWindow::disconnectFsxSimConnect();

    // close logger
    if (FsxWsMainWindow::_logger) {
        delete FsxWsMainWindow::_logger;
    }
    FsxWsMainWindow::_logger = nullptr;

    // clean up
    delete FsxWsMainWindow::_aircraftUpdateTimer;
    FsxWsMainWindow::_aircraftUpdateTimer = nullptr;
    delete FsxWsMainWindow::_configureDialog;
    FsxWsMainWindow::_configureDialog = nullptr;
    delete FsxWsMainWindow::_aboutDialog;
    FsxWsMainWindow::_aboutDialog = nullptr;
    delete FsxWsMainWindow::_deltaWaypointsViewDataModel;
    FsxWsMainWindow::_deltaWaypointsViewDataModel = nullptr;
    delete FsxWsMainWindow::_waypointsViewDataModel;
    FsxWsMainWindow::_waypointsViewDataModel = nullptr;
}

//
// Init misc stuff
//
void FsxWsMainWindow::init()
{
    FsxWsMainWindow::_webServerDispatcher = nullptr;
    FsxWsMainWindow::_fsxSimConnect = nullptr;
    FsxWsMainWindow::_fsxWsSettings = nullptr;
    FsxWsMainWindow::_aircraftsViewDataModel = nullptr;
    FsxWsMainWindow::_waypointsViewDataModel = nullptr;
    FsxWsMainWindow::_deltaWaypointsViewDataModel = nullptr;
    FsxWsMainWindow::_aircraftUpdateTimer = nullptr;
    FsxWsMainWindow::_logger = nullptr;
    FsxWsMainWindow::_aboutDialog = nullptr;
    FsxWsMainWindow::_configureDialog = nullptr;
    FsxWsMainWindow::_metaDataRegistered = false;
    FsxWsMainWindow::Version = QString(VERSION);

    // order here is crucial
    FsxWsMainWindow::initLogger();
    FsxWsMainWindow::initGUI();
    FsxWsMainWindow::initSettings();

    // checks
    FsxWsMainWindow::startupCheck();
    FsxWsMainWindow::slotLogMessage("Init completed for version " + FsxWsMainWindow::Version, QtDebugMsg);

    // delayed init timer
    if (FsxWsMainWindow::_fsxWsSettings->isAutoConnectFsx() || FsxWsMainWindow::_fsxWsSettings->isAutoStartWebServer()) {
        FsxWsMainWindow::setEnabled(false); // disbale window until auto init is completed
        QTimer::singleShot(2000, this, SLOT(slotDelayedInit()));
    }
}

//
// Init the signals, recalled when FSX is connected
//
void FsxWsMainWindow::initGUI2FSXSignalsAndMetadata()
{
    // Signal for Aircraft timer is initialized in the initAircraftUpdateTimer() method
    if (!FsxWsMainWindow::_metaDataRegistered) {
        qRegisterMetaType<Aircraft>("Aircraft");
        qRegisterMetaType<AircraftMap>("AircraftMap");
        qRegisterMetaType<Waypoint>("Waypoint");
        qRegisterMetaType<WaypointList>("WaypointList");
        qRegisterMetaType<JsonParameter>("JsonParameter");
        qRegisterMetaType<KmlParameter>("KmlParameter");
        FsxWsMainWindow::_metaDataRegistered = true;
    }

    this->disconnect(FsxWsMainWindow::_fsxSimConnect);
    FsxWsMainWindow::_fsxSimConnect->disconnect(this);

    // this -> simConnect
    bool s = QObject::connect(this, SIGNAL(signalFSXAircrafts()),
                              FsxWsMainWindow::_fsxSimConnect, SLOT(slotAircrafts()), Qt::AutoConnection);
    s = QObject::connect(this, SIGNAL(signalFSXFindById(const SIMCONNECT_OBJECT_ID)),
                         FsxWsMainWindow::_fsxSimConnect, SLOT(slotFindAircraftById(const SIMCONNECT_OBJECT_ID)), Qt::AutoConnection);
    s = QObject::connect(ui->qtvAircraftList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
                         this, SLOT(onAircraftListSelectionChanged(const QItemSelection&, const QItemSelection&)), Qt::AutoConnection);
    s = QObject::connect(this, SIGNAL(signalFSXAircraftsJsonData(const JsonParameter&)),
                         FsxWsMainWindow::_fsxSimConnect, SLOT(slotAircraftsJsonArray(const JsonParameter&)), Qt::AutoConnection);
    s = QObject::connect(this, SIGNAL(signalFSXAircraftsHtmlTable()),
                         FsxWsMainWindow::_fsxSimConnect, SLOT(slotAircraftsHtmlTable()), Qt::AutoConnection);

    // simConnect -> this
    // Currently only working if Qt::DirectConnection, even considered FsxSimConnect runs in its own thread
    // Needs to be figured out .....
    s = QObject::connect(FsxWsMainWindow::_fsxSimConnect, SIGNAL(signalLogMessage(const QString&, QtMsgType)),
                         this, SLOT(slotLogMessage(const QString&, QtMsgType)), Qt::AutoConnection);
    s = QObject::connect(FsxWsMainWindow::_fsxSimConnect, SIGNAL(signalMessageLoopStopped()),
                         this, SLOT(slotFSXMessageLoopStopped()), Qt::AutoConnection);
    s = QObject::connect(FsxWsMainWindow::_fsxSimConnect, SIGNAL(signalQuit()),
                         this, SLOT(slotFSXQuit()), Qt::AutoConnection);
}

//
// Init the GUI
//
void FsxWsMainWindow::initGUI()
{
    FsxWsMainWindow::loggerMessage("initGUI", QtDebugMsg);
    ui->qtwMainTabPanel->setCurrentIndex(0);

    // add dialogs
    FsxWsMainWindow::_configureDialog = new FsxWsConfigureDialog(this);
    FsxWsMainWindow::_configureDialog->setModal(true);
    FsxWsMainWindow::_aboutDialog = new FsxWsAbout(this);
    FsxWsMainWindow::_aboutDialog->setModal(true);

    // init further details of GUI
    FsxWsMainWindow::initWebServiceButtons();
    FsxWsMainWindow::initFsxSimConnectButtons();
    FsxWsMainWindow::initAircraftsTableView();
    FsxWsMainWindow::initWaypointsTableView();
    FsxWsMainWindow::initAircraftUpdateTimer();
    FsxWsMainWindow::initContextMenus();

    FsxWsMainWindow::loggerMessage("initGUI completed", QtDebugMsg);
}

//
// Context menus
//
void FsxWsMainWindow::initContextMenus() {
    QAction* actWaypoints = new QAction(tr("Waypoints"), this);
    QAction* actSaveKml = new QAction(tr("Save KML"), this);
    actWaypoints->setStatusTip(tr("Goto waypointa"));
    actSaveKml->setStatusTip(tr("Save waypoints for Google Earth"));
    ui->qtvAircraftList->setContextMenuPolicy(Qt::ActionsContextMenu);
    ui->qtvAircraftList->addAction(actWaypoints);
    ui->qtvAircraftList->addAction(actSaveKml);
    connect(actWaypoints, SIGNAL(triggered()), this, SLOT(slotSetWaypointsTab()));
    connect(actSaveKml, SIGNAL(triggered()), this, SLOT(onMenuFSXSaveKmlData()));
}

//
// Init settings
//
void FsxWsMainWindow::initSettings()
{
    FsxWsMainWindow::loggerMessage("initSettings", QtDebugMsg);
    FsxWsMainWindow::_fsxWsSettings = new FsxWsSettings(FsxWsMainWindow::parent());
    if (FsxWsMainWindow::_fsxWsSettings->read()) {
        FsxWsMainWindow::loggerMessage("Settings read", QtDebugMsg);
    } else {
        FsxWsMainWindow::loggerMessage("No settings found", QtWarningMsg);
    }
    if (FsxWsMainWindow::_configureDialog) FsxWsMainWindow::_configureDialog->setSettings(FsxWsMainWindow::_fsxWsSettings);
}

//
// Init the aircrafts table view
//
void FsxWsMainWindow::initAircraftsTableView()
{
    if (!FsxWsMainWindow::_aircraftsViewDataModel)
        FsxWsMainWindow::_aircraftsViewDataModel = new QStandardItemModel(this);
    else
        FsxWsMainWindow::_aircraftsViewDataModel->clear();

    int idx=0;
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Callsign"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Model"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Type"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Id"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Upd.UTC"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Updated"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Wps."));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Squawk"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Latitude"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Longitude"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Alt(ft)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Height(ft)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("ind.Alt(ft)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Elev.(m)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("QNH(mb)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("GS(kts)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("TAS(kts)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("IAS(kts)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Hdg(true)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Pitch"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Bank"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("AoA"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Sideslip"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Gnd"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Surface"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Com1 (MHz)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Com2 (MHz)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Nav1 (MHz)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("Nav2 (MHz)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("ADF1 (kHz)"));
    FsxWsMainWindow::_aircraftsViewDataModel->setHorizontalHeaderItem(idx++, new QStandardItem("ADF2 (kHz)"));
    ui->qtvAircraftList->setModel(FsxWsMainWindow::_aircraftsViewDataModel);
    ui->qtvAircraftList->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
    ui->qtvAircraftList->setEditTriggers(QAbstractItemView::NoEditTriggers); // read only
    ui->qtvAircraftList->setSortingEnabled(true);
}

//
// Init the waypoints table view
//
void FsxWsMainWindow::initWaypointsTableView()
{
    if (!FsxWsMainWindow::_waypointsViewDataModel) FsxWsMainWindow::_waypointsViewDataModel = new QStandardItemModel(this);
    if (!FsxWsMainWindow::_deltaWaypointsViewDataModel) FsxWsMainWindow::_deltaWaypointsViewDataModel = new QStandardItemModel(this);

    FsxWsMainWindow::initWaypointsTableView(FsxWsMainWindow::_waypointsViewDataModel, ui->qtvWaypointList);
    FsxWsMainWindow::initWaypointsTableView(FsxWsMainWindow::_deltaWaypointsViewDataModel, ui->qtvDeltaWaypointList);
}

//
// Init the waypoint table views
//
void FsxWsMainWindow::initWaypointsTableView(QStandardItemModel* itemModel, QTableView* tableView) {
    int idx=0;
    itemModel->clear();
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("UTC"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Time"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Latitude"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Longitude"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Alt(ft)"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Gnd"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("GS(kts)"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("TAS(kts)"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("IAS(kts)"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Pitch"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Bank"));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Pos./Spd."));
    itemModel->setHorizontalHeaderItem(idx++, new QStandardItem("Instr."));
    tableView->setModel(itemModel);
    tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); // read only
    tableView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
    tableView->setSortingEnabled(true);
}

//
// Init the Aircraft update timer
//
void FsxWsMainWindow::initAircraftUpdateTimer()
{
    bool running = FsxWsMainWindow::_fsxSimConnect && FsxWsMainWindow::_fsxSimConnect->isRunning();
    if (!FsxWsMainWindow::_aircraftUpdateTimer) {
        // 1st init
        FsxWsMainWindow::_aircraftUpdateTimer = new QTimer(this);
        connect(FsxWsMainWindow::_aircraftUpdateTimer, SIGNAL(timeout()), this, SLOT(slotUpdateAircrafts()), Qt::QueuedConnection);
    } else {
        // new start will be set below
        FsxWsMainWindow::_aircraftUpdateTimer->stop();
    }
    if (running)FsxWsMainWindow::_aircraftUpdateTimer->start(FsxWsMainWindow::_fsxWsSettings->getGuiUpdateAircraftsMs());
}

//
// Init the logger
//
void FsxWsMainWindow::initLogger()
{
    QString configFileName = QApplication::applicationDirPath() + "/config/FsxWsLogging.ini";
    if (QFile::exists(configFileName)) {

        // Fix log dir
        QString logDirPath = QApplication::applicationDirPath();
        logDirPath.append("/logs"); // this must match the settings in FsxWsLogging.ini
        QDir logDir(logDirPath);
        if (!logDir.exists()) {
            if (logDir.mkdir(logDirPath))
                FsxWsMainWindow::slotLogMessage("Created log dir: " + logDirPath, QtDebugMsg);
            else
                FsxWsMainWindow::slotLogMessage("Cannot create log dir: " + logDirPath, QtWarningMsg);
        } else
            FsxWsMainWindow::slotLogMessage("Log dir: " + logDirPath, QtDebugMsg);

        // Configure logging into files
        QSettings* mainLogSettings=new QSettings(configFileName,QSettings::IniFormat,this);
        mainLogSettings->beginGroup("mainLogFile");
        QSettings* debugLogSettings=new QSettings(configFileName,QSettings::IniFormat,this);
        debugLogSettings->beginGroup("debugLogFile");
        FsxWsMainWindow::_logger=new DualFileLogger(mainLogSettings,debugLogSettings,10000,this);
        FsxWsMainWindow::_logger->installMsgHandler();
        FsxWsMainWindow::loggerMessage(QString("Logger with config from %1").arg(configFileName), QtDebugMsg);
    } else {
        FsxWsMainWindow::_logger = nullptr;
        qDebug("No logger ini file, logger not available!");
    }
}

//
// Delayed init, allows to trigger some initializations when system is ready
//
void FsxWsMainWindow::slotDelayedInit() {
    if (!FsxWsMainWindow::_fsxWsSettings) {
        FsxWsMainWindow::slotLogMessage("Settings not yet ready, cancel deleyed init!", QtCriticalMsg);
        return;
    }

    // auto starts
    if(FsxWsMainWindow::_fsxWsSettings->isAutoStartWebServer()) FsxWsMainWindow::onStartWebServiceClicked();
    if(FsxWsMainWindow::_fsxWsSettings->isAutoConnectFsx()) FsxWsMainWindow::onConnectFSXClicked();
    FsxWsMainWindow::setEnabled(true);
}

//
// Init the WebService buttons correctly
//
void FsxWsMainWindow::initWebServiceButtons()
{
    bool running = FsxWsMainWindow::_webServerDispatcher;
    ui->qpbStartWebService->setEnabled(!running);
    ui->qpbStopWebService->setEnabled(running);
}

//
// Set the SimConnect buttons accordingly
//
void FsxWsMainWindow::initFsxSimConnectButtons()
{
    bool running = FsxWsMainWindow::_fsxSimConnect && FsxWsMainWindow::_fsxSimConnect->isRunning();
    ui->qpbConnectFSX->setEnabled(!running);
    ui->qpbDisconnectFSX->setEnabled(running);
    ui->qaMenuFSXSaveJsonData->setEnabled(running);
    if (!running) {
        // only disable, enabled in FsxWsMainWindow::onAircraftListSelectionChanged
        ui->qaMenuFSXSaveWaypoinsKml->setEnabled(false);
        ui->qaMenuFSXSaveWaypoinsKmlReducedResolution->setEnabled(false);
    }
    FsxWsMainWindow::initAircraftUpdateTimer();
}

//
// Testing "some" prerequisites and display info
//
void FsxWsMainWindow::startupCheck() {
    FsxWsMainWindow::loggerMessage("Startup check", QtDebugMsg);
    QString fileName = FSXHelper::getLocalSimConnectCfgFilename();
    if (QFile::exists(fileName)) {
        FsxWsMainWindow::slotLogMessage("Local config file exists: " + fileName, QtDebugMsg);
    } else {
        FsxWsMainWindow::slotLogMessage("No local SimConnect.cfg, using external config", QtDebugMsg);
    }

    QString fmcDir = FsxWsMainWindow::_fsxWsSettings->getFmcDataDirectory();
    if (Utils::isValidFmcDataDirectory(fmcDir)) {
        FsxWsMainWindow::slotLogMessage("FMC data directory exists: " + fmcDir, QtDebugMsg);
    } else {
        if (fmcDir.isEmpty())
            FsxWsMainWindow::slotLogMessage("FMC data directory is empty", QtWarningMsg);
        else
            FsxWsMainWindow::slotLogMessage("FMC data directory does not exists: " + fmcDir, QtWarningMsg);
    }
}
